home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / DEMOS / PARTICLE / PARTICLE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  13.0 KB  |  636 lines

  1. /* 
  2.     particle.c
  3.     Nate Robins, 1997
  4.  
  5.     An example of a simple particle system.
  6.  
  7.  */
  8.  
  9.  
  10. #include <math.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <GL/glut.h>
  15.  
  16.  
  17. #ifdef _WIN32
  18. #define drand48() ((float)rand()/RAND_MAX)
  19. #endif
  20.  
  21.  
  22. /* #define SCREEN_SAVER_MODE */
  23.  
  24. #define PS_GRAVITY -9.8
  25. #define PS_WATERFALL 0
  26. #define PS_FOUNTAIN  1
  27.  
  28.  
  29. typedef struct {
  30.     float x, y, z;
  31.     float radius;
  32. } PSsphere;
  33.  
  34. typedef struct {
  35.     float position[3];            /* current position */
  36.     float previous[3];            /* previous position */
  37.     float velocity[3];            /* velocity (magnitude & direction) */
  38.     float dampening;            /* % of energy lost on collision */
  39.     int alive;                /* is this particle alive? */
  40. } PSparticle;
  41.  
  42.  
  43. PSparticle* particles = NULL;
  44. PSsphere    sphere = { 0, 1, 0, 0.25 };
  45. int num_particles = 5000;
  46. int type = PS_WATERFALL;
  47. int points = 1;
  48. int do_sphere = 0;
  49. int frame_rate = 1;
  50. float frame_time = 0;
  51. float flow = 500;
  52. float slow_down = 1;
  53.  
  54. float spin_x = 0;
  55. float spin_y = 0;
  56. int point_size = 3;
  57.  
  58.  
  59. /* timedelta: returns the number of seconds that have elapsed since
  60.    the previous call to the function. */
  61. float
  62. timedelta(void)
  63. {
  64.     static long begin = 0;
  65.     static long finish, difference;
  66.  
  67. #if defined(_WIN32)
  68. #include <sys/timeb.h>
  69.     static struct timeb tb;
  70.  
  71.     ftime(&tb);
  72.     finish = tb.time*1000+tb.millitm;
  73. #else
  74. #include <limits.h>
  75. #include <unistd.h>
  76. #include <sys/types.h>
  77. #include <sys/times.h>
  78.     static struct tms tb;
  79.  
  80.     finish = times(&tb);
  81. #endif
  82.  
  83.     difference = finish - begin;
  84.     begin = finish;
  85.  
  86.     return (float)difference/(float)1000;  /* CLK_TCK=1000 */
  87. }
  88.  
  89.  
  90. /* text: draws a string of text with an 18 point helvetica bitmap font
  91.    at position (x,y) in window space (bottom left corner is (0,0). */
  92. void
  93. text(int x, int y, char* s) 
  94. {
  95.     int lines;
  96.     char* p;
  97.  
  98.     glDisable(GL_DEPTH_TEST);
  99.     glMatrixMode(GL_PROJECTION);
  100.     glPushMatrix();
  101.     glLoadIdentity();
  102.     glOrtho(0, glutGet(GLUT_WINDOW_WIDTH), 
  103.         0, glutGet(GLUT_WINDOW_HEIGHT), -1, 1);
  104.     glMatrixMode(GL_MODELVIEW);
  105.     glPushMatrix();
  106.     glLoadIdentity();
  107.     glColor3ub(0, 0, 0);
  108.     glRasterPos2i(x+1, y-1);
  109.     for(p = s, lines = 0; *p; p++) {
  110.     if (*p == '\n') {
  111.         lines++;
  112.         glRasterPos2i(x+1, y-1-(lines*18));
  113.     }
  114.     glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *p);
  115.     }
  116.     glColor3ub(128, 0, 255);
  117.     glRasterPos2i(x, y);
  118.     for(p = s, lines = 0; *p; p++) {
  119.     if (*p == '\n') {
  120.         lines++;
  121.         glRasterPos2i(x, y-(lines*18));
  122.     }
  123.     glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *p);
  124.     }
  125.     glMatrixMode(GL_PROJECTION);
  126.     glPopMatrix();
  127.     glMatrixMode(GL_MODELVIEW);
  128.     glPopMatrix();
  129.     glEnable(GL_DEPTH_TEST);
  130. }
  131.  
  132.  
  133. int
  134. fequal(float a, float b)
  135. {
  136.     float epsilon = 0.1;
  137.     float f = a - b;
  138.     
  139.     if (f < epsilon && f > -epsilon)
  140.     return 1;
  141.     else
  142.     return 0;
  143. }
  144.  
  145.  
  146. void
  147. psTimeStep(PSparticle* p, float dt)
  148. {
  149.     if (p->alive == 0)
  150.     return;
  151.  
  152.     p->velocity[0] += 0;
  153.     p->velocity[1] += PS_GRAVITY*dt;
  154.     p->velocity[2] += 0;
  155.  
  156.     p->previous[0] = p->position[0];
  157.     p->previous[1] = p->position[1];
  158.     p->previous[2] = p->position[2];
  159.  
  160.     p->position[0] += p->velocity[0]*dt;
  161.     p->position[1] += p->velocity[1]*dt;
  162.     p->position[2] += p->velocity[2]*dt;
  163. }
  164.  
  165.  
  166. void
  167. psNewParticle(PSparticle* p, float dt)
  168. {
  169.     if (type == PS_WATERFALL) {
  170.     p->velocity[0] = 1*(drand48()-0.5);
  171.     p->velocity[1] = 0;
  172.     p->velocity[2] = 0.5*(drand48()-0.0);
  173.     p->position[0] = 0;
  174.     p->position[1] = 2;
  175.     p->position[2] = 0;
  176.     p->previous[0] = p->position[0];
  177.     p->previous[1] = p->position[1];
  178.     p->previous[2] = p->position[2];
  179.     p->dampening = 0.45*drand48();
  180.     p->alive = 1;
  181.     } else if (type == PS_FOUNTAIN) {
  182.     p->velocity[0] = 2*(drand48()-0.5);
  183.     p->velocity[1] = 6;
  184.     p->velocity[2] = 2*(drand48()-0.5);
  185.     p->position[0] = 0;
  186.     p->position[1] = 0;
  187.     p->position[2] = 0;
  188.     p->previous[0] = p->position[0];
  189.     p->previous[1] = p->position[1];
  190.     p->previous[2] = p->position[2];
  191.     p->dampening = 0.35*drand48();
  192.     p->alive = 1;
  193.     }
  194.  
  195.     psTimeStep(p, 2*dt*drand48());
  196. }
  197.  
  198.  
  199. /* psBounce: the particle has gone past (or exactly hit) the ground
  200.    plane, so calculate the time at which the particle actually
  201.    intersected the ground plane (s).  essentially, this just rolls
  202.    back time to when the particle hit the ground plane, then starts
  203.    time again from then.
  204.  
  205.    -  -   o A  (previous position)
  206.    |  |    \
  207.    |  s     \   o  (position it _should_ be at) -
  208.    t  |      \ /                                | t - s 
  209.    |  - ------X--------                         -
  210.    |           \
  211.    -            o B  (new position)
  212.                
  213.    A + V*s = 0 or s = -A/V
  214.  
  215.    to calculate where the particle should be:
  216.  
  217.    A + V*t + V*(t-s)*d
  218.  
  219.    where d is a damping factor which accounts for the loss
  220.    of energy due to the bounce. */
  221. void
  222. psBounce(PSparticle* p, float dt)
  223. {
  224.     float s;
  225.  
  226.     if (p->alive == 0)
  227.     return;
  228.  
  229.     /* since we know it is the ground plane, we only need to
  230.        calculate s for a single dimension. */
  231.     s = -p->previous[1]/p->velocity[1];
  232.  
  233.     p->position[0] = (p->previous[0] + p->velocity[0] * s + 
  234.               p->velocity[0] * (dt-s) * p->dampening);
  235.     p->position[1] = -p->velocity[1] * (dt-s) * p->dampening; /* reflect */
  236.     p->position[2] = (p->previous[2] + p->velocity[2] * s + 
  237.               p->velocity[2] * (dt-s) * p->dampening);
  238.  
  239.     /* damp the reflected velocity (since the particle hit something,
  240.        it lost some energy) */
  241.     p->velocity[0] *=  p->dampening;
  242.     p->velocity[1] *= -p->dampening;        /* reflect */
  243.     p->velocity[2] *=  p->dampening;
  244. }
  245.  
  246. void
  247. psCollide(PSparticle* p)
  248. {
  249.     float vx = p->position[0] - sphere.x;
  250.     float vy = p->position[1] - sphere.y;
  251.     float vz = p->position[2] - sphere.z;
  252.     float distance;
  253.  
  254.     if (p->alive == 0)
  255.     return;
  256.  
  257.     distance = sqrt(vx*vx + vy*vy + vz*vz);
  258.  
  259.     if (distance < sphere.radius) {
  260. #if 0
  261.     vx /= distance;  vy /= distance;  vz /= distance;
  262.     d = 2*(-vx*p->velocity[0] + -vy*p->velocity[1] + -vz*p->velocity[2]);
  263.     p->velocity[0] += vx*d*2;
  264.     p->velocity[1] += vy*d*2;
  265.     p->velocity[2] += vz*d*2;
  266.     d = sqrt(p->velocity[0]*p->velocity[0] + 
  267.          p->velocity[1]*p->velocity[1] +
  268.          p->velocity[2]*p->velocity[2]);
  269.     p->velocity[0] /= d;
  270.     p->velocity[1] /= d;
  271.     p->velocity[2] /= d;
  272. #else
  273.     p->position[0] = sphere.x+(vx/distance)*sphere.radius;
  274.     p->position[1] = sphere.y+(vy/distance)*sphere.radius;
  275.     p->position[2] = sphere.z+(vz/distance)*sphere.radius;
  276.     p->previous[0] = p->position[0];
  277.     p->previous[1] = p->position[1];
  278.     p->previous[2] = p->position[2];
  279.     p->velocity[0] = vx/distance;
  280.     p->velocity[1] = vy/distance;
  281.     p->velocity[2] = vz/distance;
  282. #endif
  283.     }
  284. }
  285.  
  286.  
  287. void
  288. reshape(int width, int height)
  289. {
  290.     float black[] = { 0, 0, 0, 0 };
  291.  
  292.     glViewport(0, 0, width, height);
  293.     glMatrixMode(GL_PROJECTION);
  294.     glLoadIdentity();
  295.     gluPerspective(60, 1, 0.1, 1000);
  296.     glMatrixMode(GL_MODELVIEW);
  297.     glLoadIdentity();
  298.     gluLookAt(0, 1, 3, 0, 1, 0, 0, 1, 0);
  299.     glFogfv(GL_FOG_COLOR, black);
  300.     glFogf(GL_FOG_START, 2.5);
  301.     glFogf(GL_FOG_END, 4);
  302.     glEnable(GL_FOG);
  303.     glFogi(GL_FOG_MODE, GL_LINEAR);
  304.     glPointSize(point_size);
  305.     glEnable(GL_POINT_SMOOTH);
  306.     glEnable(GL_BLEND);
  307.     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  308.     glEnable(GL_COLOR_MATERIAL);
  309.     glEnable(GL_DEPTH_TEST);
  310.     glEnable(GL_LIGHT0);
  311.  
  312.     timedelta();
  313. }
  314.  
  315.  
  316. void
  317. display(void)
  318. {
  319.     static int i;
  320.     static float c;
  321.     static char s[32];
  322.     static int frames = 0;
  323.  
  324.     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  325.     glPushMatrix();
  326.  
  327.     glRotatef(spin_y, 1, 0, 0);
  328.     glRotatef(spin_x, 0, 1, 0);
  329.  
  330.     glEnable(GL_LIGHTING);
  331.     if (do_sphere) {
  332.     glPushMatrix();
  333.     glTranslatef(sphere.x, sphere.y, sphere.z);
  334.     glColor3ub(0, 255, 128);
  335.     glutSolidSphere(sphere.radius, 16, 16);
  336.     glPopMatrix();
  337.     }
  338.     glDisable(GL_LIGHTING);
  339.  
  340.     glBegin(GL_QUADS);
  341.     glColor3ub(0, 128, 255);
  342.     glVertex3f(-2, 0, -2);
  343.     glVertex3f(-2, 0, 2);
  344.     glVertex3f(2, 0, 2);
  345.     glVertex3f(2, 0, -2);
  346.     glEnd();
  347.  
  348.     if (points) {
  349.     glBegin(GL_POINTS);
  350.     
  351.     for (i = 0; i < num_particles; i++) {
  352.         if (particles[i].alive == 0)
  353.         continue;
  354.         c = particles[i].position[1]/2.1*255;
  355.         glColor3ub((GLubyte) c, (GLubyte) (128+c*0.5), 255);
  356.         glVertex3fv(particles[i].position);
  357.     }
  358.     glEnd();
  359.     } else {
  360.     glBegin(GL_LINES);
  361.     for (i = 0; i < num_particles; i++) {
  362.         if (particles[i].alive == 0)
  363.         continue;
  364.         c = particles[i].previous[1]/2.1*255;
  365.         glColor3ub((GLubyte) c, (GLubyte) (128+c*0.5), 255);
  366.         glVertex3fv(particles[i].previous);
  367.         c = particles[i].position[1]/2.1*255;
  368.         glColor3ub((GLubyte) c, (GLubyte) (128+c*0.5), 255);
  369.         glVertex3fv(particles[i].position);
  370.     }
  371.     glEnd();
  372.     }
  373.  
  374.     /* spit out frame rate. */
  375.     if (frame_rate) {
  376.     frames++;
  377.     if (frames > 7) {
  378.         sprintf(s, "%g fps", (float)7/frame_time);
  379.         frame_time = 0;
  380.         frames = 0;
  381.     }
  382.     text(5, 5, s);
  383.     }
  384.  
  385.     glPopMatrix();
  386.     glutSwapBuffers();
  387. }
  388.  
  389. void
  390. idleFunc(void)
  391. {
  392.     static int i;
  393.     static int living = 0;        /* index to end of live particles */
  394.     static float dt;
  395.  
  396.     dt = timedelta();
  397.     frame_time += dt;
  398.  
  399. #if 1
  400.     /* slow the simulation if we can't keep the frame rate up around
  401.        10 fps */
  402.     if (dt > 0.1) {
  403.     slow_down = 0.75;
  404.     } else if (dt < 0.1) {
  405.     slow_down = 1;
  406.     }
  407. #endif
  408.  
  409.     dt *= slow_down;
  410.  
  411.     /* resurrect a few particles */
  412.     for (i = 0; i < flow*dt; i++) {
  413.     psNewParticle(&particles[living], dt);
  414.     living++;
  415.     if (living >= num_particles)
  416.         living = 0;
  417.     }
  418.  
  419.     for (i = 0; i < num_particles; i++) {
  420.     psTimeStep(&particles[i], dt);
  421.  
  422.     /* collision with sphere? */
  423.     if (do_sphere) {
  424.         psCollide(&particles[i]);
  425.     }
  426.  
  427.     /* collision with ground? */
  428.     if (particles[i].position[1] <= 0) {
  429.         psBounce(&particles[i], dt);
  430.     }
  431.  
  432.     /* dead particle? */
  433.     if (particles[i].position[1] < 0.1 && 
  434.         fequal(particles[i].velocity[1], 0)) {
  435.         particles[i].alive = 0;
  436.     }
  437.     }
  438.  
  439.     glutPostRedisplay();
  440. }
  441.  
  442. void
  443. visible(int state)
  444. {
  445.     if (state == GLUT_VISIBLE) {
  446.         timedelta();
  447.     glutIdleFunc(idleFunc);
  448.     } else {
  449.     glutIdleFunc(NULL);
  450.     }
  451. }
  452.  
  453. void
  454. bail(int code)
  455. {
  456.     free(particles);
  457.     exit(code);
  458. }
  459.  
  460. #ifdef SCREEN_SAVER_MODE
  461. /* ARGSUSED */
  462. void
  463. ss_keyboard(char key, int x, int y)
  464. {
  465.     bail(0);
  466. }
  467.  
  468. /* ARGSUSED */
  469. void
  470. ss_mouse(int button, int state, int x, int y)
  471. {
  472.     bail(0);
  473. }
  474.  
  475. /* ARGSUSED */
  476. void
  477. ss_passive(int x, int y)
  478. {
  479.     static int been_here = 0;
  480.  
  481.     /* for some reason, GLUT sends an initial passive motion callback
  482.        when a window is initialized, so this would immediately
  483.        terminate the program.  to get around this, see if we've been
  484.        here before. (actually if we've been here twice.) */
  485.  
  486.     if (been_here > 1)
  487.     bail(0);
  488.     been_here++;
  489. }
  490.  
  491. #else
  492.  
  493. /* ARGSUSED1 */
  494. void
  495. keyboard(unsigned char key, int x, int y)
  496. {
  497.     static int fullscreen = 0;
  498.     static int old_x = 50;
  499.     static int old_y = 50;
  500.     static int old_width = 320;
  501.     static int old_height = 320;
  502.  
  503.     switch (key) {
  504.     case 27:
  505.         bail(0);
  506.     break;
  507.  
  508.     case 'w':
  509.     type = PS_WATERFALL;
  510.     break;
  511.  
  512.     case 'f':
  513.     type = PS_FOUNTAIN;
  514.     break;
  515.  
  516.     case 's':
  517.     do_sphere = !do_sphere;
  518.     break;
  519.  
  520.     case 'l':
  521.     points = !points;
  522.     break;
  523.  
  524.     case 'P':
  525.     point_size++;
  526.     glPointSize(point_size);
  527.     break;
  528.     
  529.     case 'p':
  530.     point_size--;
  531.     if (point_size < 1)
  532.         point_size = 1;
  533.     glPointSize(point_size);
  534.     break;
  535.     
  536.     case '+':
  537.     flow += 100;
  538.     if (flow > num_particles)
  539.         flow = num_particles;
  540.     printf("%g particles/second\n", flow);
  541.     break;
  542.  
  543.     case '-':
  544.     flow -= 100;
  545.     if (flow < 0)
  546.         flow = 0;
  547.     printf("%g particles/second\n", flow);
  548.     break;
  549.  
  550.     case '~':
  551.     fullscreen = !fullscreen;
  552.     if (fullscreen) {
  553.         old_x = glutGet(GLUT_WINDOW_X);
  554.         old_y = glutGet(GLUT_WINDOW_Y);
  555.         old_width = glutGet(GLUT_WINDOW_WIDTH);
  556.         old_height = glutGet(GLUT_WINDOW_HEIGHT);
  557.         glutFullScreen();
  558.     } else {
  559.         glutReshapeWindow(old_width, old_height);
  560.         glutPositionWindow(old_x, old_y);
  561.     }
  562.     break;
  563.     }
  564. }
  565.  
  566. #endif
  567.  
  568. int old_x, old_y;
  569.  
  570. /* ARGSUSED */
  571. void
  572. mouse(int button, int state, int x, int y)
  573. {
  574.     old_x = x;
  575.     old_y = y;
  576.  
  577.     glutPostRedisplay();
  578. }
  579.  
  580. void
  581. motion(int x, int y)
  582. {
  583.     spin_x = x - old_x;
  584.     spin_y = y - old_y;
  585.  
  586.     glutPostRedisplay();
  587. }
  588.  
  589. int
  590. main(int argc, char** argv)
  591. {
  592.     glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH|GLUT_DOUBLE);
  593.     glutInitWindowPosition(50, 50);
  594.     glutInitWindowSize(320, 320);
  595.     glutInit(&argc, argv);
  596.  
  597.     if (argc > 1 && !strcmp(argv[1], "-fullscreen")) {
  598.       glutGameModeString("640x480:16@60");
  599.       glutEnterGameMode();
  600.     } else {
  601.       glutCreateWindow("Particles");
  602.     }
  603.  
  604.     glutDisplayFunc(display);
  605.     glutReshapeFunc(reshape);
  606. #ifdef SCREEN_SAVER_MODE
  607.     glutPassiveMotionFunc(ss_passive);
  608.     glutKeyboardFunc(ss_keyboard);
  609.     glutMouseFunc(ss_mouse);
  610.     glutSetCursor(GLUT_CURSOR_NONE);
  611.     glutFullScreen(); 
  612. #else
  613.     glutMotionFunc(motion);
  614.     glutMouseFunc(mouse);
  615.     glutKeyboardFunc(keyboard);
  616. #endif
  617.  
  618.     if (argc > 1) {
  619.     if (strcmp(argv[1], "-h") == 0) {
  620.         fprintf(stderr, "%s [particles] [flow] [speed%%]\n", argv[0]);
  621.         exit(0);
  622.     }
  623.     sscanf(argv[1], "%d", &num_particles);
  624.     if (argc > 2)
  625.         sscanf(argv[2], "%f", &flow);
  626.     if (argc > 3)
  627.         sscanf(argv[3], "%f", &slow_down);
  628.     }      
  629.  
  630.     particles = (PSparticle*)malloc(sizeof(PSparticle) * num_particles);
  631.  
  632.     glutVisibilityFunc(visible);
  633.     glutMainLoop();
  634.     return 0;
  635. }
  636.